From e916274167d798da75ef8521c4302d765694d1cb Mon Sep 17 00:00:00 2001 From: Shivani Bhardwaj Date: Tue, 6 Jan 2026 16:44:52 +0530 Subject: [PATCH] [PATCH 1/3] dcerpc: add upper limit on stub data DCERPC parsers had no upper bounds when it came to extending the stub data buffer. Traffic can be crafted to bypass some internal parser conditions to create an indefinite buffering in the stub_data array that can make Suricata crash. Add a default limit of 1MiB and make it configurable for the user. Security 8182 Co-authored-by: Philippe Antoine (cherry picked from commit e412215af990feeffbb66c7dd9f392813a20ae50) Origin: upstream, https://github.com/OISF/suricata/commit/f82a388d0283725cb76782cf64e8341cab370830.patch Bug: https://redmine.openinfosecfoundation.org/issues/8182 Subject: Upstream fix for CVE-2026-22258 part 1 Gbp-Pq: Name CVE-2026-22258_1.patch --- rust/src/dcerpc/dcerpc.rs | 31 +++++++++++++++++++++++++++++-- rust/src/dcerpc/dcerpc_udp.rs | 18 +++++++++++++----- rust/src/smb/dcerpc.rs | 31 +++++++++++++++++++++++-------- rust/src/smb/smb.rs | 23 +++++++++++++++++++++++ suricata.yaml.in | 4 ++++ 5 files changed, 92 insertions(+), 15 deletions(-) diff --git a/rust/src/dcerpc/dcerpc.rs b/rust/src/dcerpc/dcerpc.rs index b99b23d3..75fc151c 100644 --- a/rust/src/dcerpc/dcerpc.rs +++ b/rust/src/dcerpc/dcerpc.rs @@ -25,7 +25,9 @@ use std; use std::cmp; use std::ffi::CString; use std::collections::VecDeque; -use crate::conf::conf_get; +use crate::conf::{conf_get, get_memval}; + +pub static mut DCERPC_MAX_STUB_SIZE: u32 = 1048576; // Constant DCERPC UDP Header length pub const DCERPC_HDR_LEN: u16 = 16; @@ -163,6 +165,11 @@ pub fn get_req_type_for_resp(t: u8) -> u8 { _ => DCERPC_TYPE_UNKNOWN, } } +#[inline(always)] +pub fn cfg_max_stub_size() -> u32 { + unsafe { DCERPC_MAX_STUB_SIZE } +} + #[derive(Default, Debug)] pub struct DCERPCTransaction { @@ -1109,7 +1116,12 @@ fn evaluate_stub_params( } let input_slice = &input[..stub_len as usize]; - stub_data_buffer.extend_from_slice(input_slice); + let max_size = cfg_max_stub_size() as usize; + if (stub_data_buffer.len() + input_slice.len()) < max_size { + stub_data_buffer.extend_from_slice(input_slice); + } else if stub_data_buffer.len() < max_size { + stub_data_buffer.extend_from_slice(&input_slice[..max_size - stub_data_buffer.len()]); + } stub_len } @@ -1409,6 +1421,21 @@ pub unsafe extern "C" fn rs_dcerpc_register_parser() { } } SCLogDebug!("Rust DCERPC parser registered."); + let retval = conf_get("app-layer.protocols.dcerpc.max-stub-size"); + if let Some(val) = retval { + match get_memval(val) { + Ok(retval) => { + if retval > 0 { + DCERPC_MAX_STUB_SIZE = retval as u32; + } else { + SCLogError!("Invalid max-stub-size value"); + } + } + Err(_) => { + SCLogError!("Invalid max-stub-size value"); + } + } + } } else { SCLogDebug!("Protocol detector and parser disabled for DCERPC."); } diff --git a/rust/src/dcerpc/dcerpc_udp.rs b/rust/src/dcerpc/dcerpc_udp.rs index 700e38fe..d551b867 100644 --- a/rust/src/dcerpc/dcerpc_udp.rs +++ b/rust/src/dcerpc/dcerpc_udp.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Open Information Security Foundation +/* Copyright (C) 2020-2026 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -19,7 +19,7 @@ use crate::applayer::{self, *}; use crate::core::{self, Direction, DIR_BOTH}; use crate::dcerpc::dcerpc::{ DCERPCTransaction, DCERPC_MAX_TX, DCERPC_TYPE_REQUEST, DCERPC_TYPE_RESPONSE, PFCL1_FRAG, PFCL1_LASTFRAG, - rs_dcerpc_get_alstate_progress, ALPROTO_DCERPC, PARSER_NAME, + rs_dcerpc_get_alstate_progress, ALPROTO_DCERPC, PARSER_NAME, cfg_max_stub_size, }; use nom7::Err; use std; @@ -171,18 +171,27 @@ impl DCERPCUDPState { tx.tx_data.updated_ts = true; let done = (hdr.flags1 & PFCL1_FRAG) == 0 || (hdr.flags1 & PFCL1_LASTFRAG) != 0; + let max_size = cfg_max_stub_size() as usize; match hdr.pkt_type { DCERPC_TYPE_REQUEST => { - tx.stub_data_buffer_ts.extend_from_slice(input); tx.frag_cnt_ts += 1; + if input.len() + tx.stub_data_buffer_ts.len() < max_size { + tx.stub_data_buffer_ts.extend_from_slice(input); + } else if tx.stub_data_buffer_ts.len() < max_size { + tx.stub_data_buffer_ts.extend_from_slice(&input[..max_size - tx.stub_data_buffer_ts.len()]); + } if done { tx.req_done = true; } return true; } DCERPC_TYPE_RESPONSE => { - tx.stub_data_buffer_tc.extend_from_slice(input); tx.frag_cnt_tc += 1; + if input.len() + tx.stub_data_buffer_tc.len() < max_size { + tx.stub_data_buffer_tc.extend_from_slice(input); + } else if tx.stub_data_buffer_tc.len() < max_size { + tx.stub_data_buffer_tc.extend_from_slice(&input[..max_size - tx.stub_data_buffer_tc.len()]); + } if done { tx.resp_done = true; } @@ -399,7 +408,6 @@ pub unsafe extern "C" fn rs_dcerpc_udp_register_parser() { } } - #[cfg(test)] mod tests { use crate::applayer::AppLayerResult; diff --git a/rust/src/smb/dcerpc.rs b/rust/src/smb/dcerpc.rs index 6c2a2f93..1e62241b 100644 --- a/rust/src/smb/dcerpc.rs +++ b/rust/src/smb/dcerpc.rs @@ -18,7 +18,7 @@ // written by Victor Julien use uuid; -use crate::smb::smb::*; +use crate::smb::smb::{cfg_max_stub_size, *}; use crate::smb::smb2::*; use crate::smb::dcerpc_records::*; use crate::smb::events::*; @@ -205,10 +205,15 @@ pub fn smb_write_dcerpc_record(state: &mut SMBState, SCLogDebug!("previous CMD {} found at tx {} => {:?}", dcer.packet_type, tx.id, tx); if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data { - SCLogDebug!("additional frag of size {}", recr.data.len()); - tdn.stub_data_ts.extend_from_slice(recr.data); tdn.frag_cnt_ts += 1; - SCLogDebug!("stub_data now {}", tdn.stub_data_ts.len()); + let max_size = cfg_max_stub_size() as usize; + if recr.data.len() + tdn.stub_data_ts.len() < max_size { + SCLogDebug!("additional frag of size {}", recr.data.len()); + tdn.stub_data_ts.extend_from_slice(recr.data); + SCLogDebug!("stub_data now {}", tdn.stub_data_ts.len()); + } else if tdn.stub_data_ts.len() < max_size { + tdn.stub_data_ts.extend_from_slice(&recr.data[..max_size - tdn.stub_data_ts.len()]); + } } if dcer.last_frag { SCLogDebug!("last frag set, so request side of DCERPC closed"); @@ -240,12 +245,17 @@ pub fn smb_write_dcerpc_record(state: &mut SMBState, SCLogDebug!("DCERPC: REQUEST {:?}", recr); if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data { SCLogDebug!("first frag size {}", recr.data.len()); - tdn.stub_data_ts.extend_from_slice(recr.data); tdn.opnum = recr.opnum; tdn.context_id = recr.context_id; tdn.frag_cnt_ts += 1; - SCLogDebug!("DCERPC: REQUEST opnum {} stub data len {}", - tdn.opnum, tdn.stub_data_ts.len()); + let max_size = cfg_max_stub_size() as usize; + if tdn.stub_data_ts.len() + recr.data.len() < max_size { + tdn.stub_data_ts.extend_from_slice(recr.data); + SCLogDebug!("DCERPC: REQUEST opnum {} stub data len {}", + tdn.opnum, tdn.stub_data_ts.len()); + } else if tdn.stub_data_ts.len() < max_size { + tdn.stub_data_ts.extend_from_slice(&recr.data[..max_size - tdn.stub_data_ts.len()]); + } } if dcer.last_frag { tx.request_done = true; @@ -407,8 +417,13 @@ fn dcerpc_response_handle(tx: &mut SMBTransaction, if let Some(SMBTransactionTypeData::DCERPC(ref mut tdn)) = tx.type_data { SCLogDebug!("CMD 11 found at tx {}", tx.id); tdn.set_result(DCERPC_TYPE_RESPONSE); - tdn.stub_data_tc.extend_from_slice(respr.data); + let max_size = cfg_max_stub_size() as usize; tdn.frag_cnt_tc += 1; + if tdn.stub_data_tc.len() + respr.data.len() < max_size { + tdn.stub_data_tc.extend_from_slice(respr.data); + } else if tdn.stub_data_tc.len() < max_size { + tdn.stub_data_tc.extend_from_slice(&respr.data[..max_size - tdn.stub_data_tc.len()]); + } } tx.vercmd.set_ntstatus(ntstatus); tx.response_done = dcer.last_frag; diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index 97d4bf4f..4a2a346d 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -81,6 +81,8 @@ pub static mut SMB_CFG_MAX_WRITE_SIZE: u32 = 16777216; pub static mut SMB_CFG_MAX_WRITE_QUEUE_SIZE: u32 = 67108864; pub static mut SMB_CFG_MAX_WRITE_QUEUE_CNT: u32 = 64; +pub static mut SMB_DCERPC_MAX_STUB_SIZE: u32 = 1048576; + static mut ALPROTO_SMB: AppProto = ALPROTO_UNKNOWN; static mut SMB_MAX_TX: usize = 1024; @@ -2436,6 +2438,21 @@ pub unsafe extern "C" fn rs_smb_register_parser() { SCLogError!("Invalid value for smb.max-tx"); } } + let retval = conf_get("app-layer.protocols.smb.dcerpc.max-stub-size"); + if let Some(val) = retval { + match get_memval(val) { + Ok(retval) => { + if retval > 0 { + SMB_DCERPC_MAX_STUB_SIZE = retval as u32; + } else { + SCLogError!("Invalid max-stub-size value"); + } + } + Err(_) => { + SCLogError!("Invalid max-stub-size value"); + } + } + } SCLogConfig!("read: max record size: {}, max queued chunks {}, max queued size {}", SMB_CFG_MAX_READ_SIZE, SMB_CFG_MAX_READ_QUEUE_CNT, SMB_CFG_MAX_READ_QUEUE_SIZE); SCLogConfig!("write: max record size: {}, max queued chunks {}, max queued size {}", @@ -2444,3 +2461,9 @@ pub unsafe extern "C" fn rs_smb_register_parser() { SCLogDebug!("Protocol detector and parser disabled for SMB."); } } + +#[inline(always)] +pub fn cfg_max_stub_size() -> u32 { + unsafe { SMB_DCERPC_MAX_STUB_SIZE } +} + diff --git a/suricata.yaml.in b/suricata.yaml.in index b49e261f..b95d5e62 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -933,6 +933,8 @@ app-layer: enabled: yes # Maximum number of live DCERPC transactions per flow # max-tx: 1024 + #max-stub-size: 1MiB + ftp: enabled: yes # memcap: 64mb @@ -997,6 +999,8 @@ app-layer: # Stream reassembly size for SMB streams. By default track it completely. #stream-depth: 0 + #dcerpc: + # max-stub-size: 1MiB nfs: enabled: yes -- 2.30.2